#include "item.hpp"
#include "weapon.hpp"
#include "image.hpp"
#include "sound.hpp"

static Image *imgs[Item::TYPECT];
static Sound *snds[Item::TYPECT];

static int AMOUNTS[Item::TYPECT] =
{
1,	5,
100,	400,	700,	1000,	2000,	4000,
Weapon::BULLET, Weapon::BUBBLE,	Weapon::CLOUD,	Weapon::BALL,	Weapon::OPTION,
6,	300,	0,	0,	1
};

#include "player.hpp"
#include "stage.hpp"

static const float ACCEL = 1.f / 32;

class Item::Type
{
public:
	virtual void Update(Item & itm)
	{
		itm.Fall();
		--itm.hp;
	}

	virtual void Hit(Item & itm, Player & plr)
	{
	}
};

class Item::Ammo : public Item::Type
{
	void Update(Item & itm)
	{
		if (itm.typen == AMMO_S)
		{
			itm.vel = Plr.pos + Plr.body.Center() - itm.pos;
			itm.vel.Norm();
			itm.vel *= .25f;
			itm.pos += itm.vel;
		}
		else
		{
			itm.Fall();
		}
		--itm.hp;
	}

	void Hit(Item & itm, Player & plr)
	{
		plr.AmmoAdd(itm.amt);
	}

	friend class Item::Melee;
}
	ammo;

#include "game.hpp"

#include "effect.hpp"

class Item::Point : public Item::Type
{
	void Update(Item & itm)
	{
		itm.Fall();
		if (!itm.Live() || itm.amt < 1000)
			--itm.hp;
	}

	void Hit(Item & itm, Player & plr)
	{
		Game.Score(itm.amt);
		Cue fxcue(itm.typen - Item::POINT + Effect::ITM_POINT_S);
		Efx.Init(itm.pos.x, itm.pos.y + itm.body.AxMin(Y), fxcue);
	}
}
	point;

class Item::Shot : public Item::Type
{
	void Hit(Item & itm, Player & plr)
	{
		plr.shottype = itm.amt;
	}
}
	shot;

class Item::Heal : public Item::Type
{
	void Hit(Item & itm, Player & plr)
	{
		plr.HPAdd(itm.amt);
	}
}
	heal;

class Item::Shield : public Item::Type
{
	void Update(Item & itm)
	{
		itm.Fall();
		if (!itm.Live())
			--itm.hp;
	}

	void Hit(Item & itm, Player & plr)
	{
		plr.shield = itm.amt;
		plr.shieldendwarn = true;
	}
}
	shield;

#include "camera.hpp"

static class Item::Nuke : public Item::Type
{
	void Update(Item & itm)
	{
		itm.Fall();
		if (!itm.Live())
			--itm.hp;
	}

	void Hit(Item & itm, Player & plr)
	{
		Cue cue (Weapon::NUKE);
		Wpns.Init(Cam.AxMin(X), Cam.AxCenter(Y), cue);
	}
}
	nuke;

class Item::StgClear : public Item::Type
{
	void Update(Item & itm)
	{
		itm.Fall();
		if (!itm.Live())
			--itm.hp;
	}

	void Hit(Item & itm, Player & plr)
	{
		Game.StateChange(Game.CLEAR);
	}
}
	stgclear;

static class Item::Melee : public Item::Type
{
	void Hit(Item & itm, Player & plr)
	{
		if (plr.meleetype < Weapon::MELEECT - 1)
			Plr.MeleeUp();
		else
		{
			Cue cue (Cue::ITEM + AMMO_L);
			itm.Init(itm.pos.x, itm.pos.y, cue);
			itm.snd = snds[AMMO];
			ammo.Hit(itm, plr);
		}
	}
}
	melee;

static Item::Type *TYPES[Item::TYPECT] =
{
&ammo,	&ammo,
&point, &point, &point, &point, &point, &point,
&shot, &shot, &shot, &shot, &shot,
&heal, &shield, &nuke, &stgclear, &melee
};

Item::Item()
{
	type = 0;
	typen = NOTYPE;
	
	amt = 0;
}

Item::~Item()
{
}

static const char *STRINGS[Item::TYPECT] =
{
	"ammos", "ammol",
	"points", "pointm", "pointl", "pointxl", "pointxxl", "pointxxxl",
	"bullet", "bubble", "cloud", "ball", "option",
	"heal", "shield", "nuke", "stgclear", "melee"
};

static const char *SNDSTRINGS[Item::TYPECT] =
{
	"ammo", "ammo",
	"point", "point", "point", "point", "point", "point",
	"shot", "shot", "shot", "shot", "shot",
	STRINGS[Item::HEAL], STRINGS[Item::SHIELD], NULL, NULL, "shot"
};

bool Item::Open()
{
	const char *DIR = "item/";

	int got = AllImgs.BatchGet(imgs, TYPECT, DIR, STRINGS);
	got += AllSnds.BatchGet(snds, TYPECT, DIR, SNDSTRINGS);
	return true;
}

static const float SIZE = 14.f/8;

#include "cue.hpp"

void Item::Init(float x, float y, Cue & cue)
{
	typen = cue.TypeN(Cue::ITEM);
	type = TYPES[typen];
	hp = 300;
	amt = AMOUNTS[typen];
	pos = Vec2f(x, y);
	body.size = Vec2f(SIZE, SIZE);
	body.off = body.size * -.5f;
	vel.y = ACCEL;
	img = imgs[typen];
	if (TSecret(typen))
	{
		alpha = 0;
	}
}

#include "video.hpp"

void Item::Update()
{
	if (Spawning())
	{
		alpha += 1.f/64;
	}
	else
	{
		if (type)
			type->Update(*this);
	}
	
	if (Gone())
		Bury();
}

void Item::Hit(Player & plr)
{
	if (!Spawning())
	{
		Kill();
		snd = snds[typen];
		if (type)
			type->Hit(*this, plr);
	}
}

void Item::Fall()
{
	vel.y = std::min(vel.y + ACCEL, 1.f);
	for (int axis = X; axis < AXISCT; ++axis)
	{
		pos[axis] += vel[axis];
		stghit[axis] = Stg.AxisHit(body, pos, axis);
		if (StgHitting(axis))
			pos[axis] -= stghit[axis];
	}
	if (StgHitting(Y))
	{
		vel.y = 0;
	}
}

ActPool<Item, 12> Itms;
